from typing import Optional, Tuple
from selenium import webdriver
from time import sleep
from pprint import pprint
import regex as re
import time
import os
import pickle
import difflib

class drv:
    def __init__(self):
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
        chrome_options.add_argument("disable-cache")
        chrome_options.add_argument('disable-infobars')
        chrome_options.add_argument('log-level=3')
        chrome_options.add_argument('--start-maximized')
        self.__driver = webdriver.Chrome(chrome_options=chrome_options)
        self.debug = True
        self.info = {}
        self.info_data = {}

    def __del__(self):
        pass
        
    @property
    def bot(self):
        return self.__bot
    
    @property
    def driver(self):
        return self.__driver

    @property
    def time(self):
        return time.strftime('%m-%d %H:%M',time.localtime(time.time())) 
    
    def getElementByXpath(self, xpath):
        try:
            element = self.driver.find_element_by_xpath(xpath)
            if element.is_displayed():
                if self.debug == True:
                    print("getElement success:%s" % element.text)
                return element
        except Exception as ex:
            if self.debug == True:
                print(ex)
            return None
        return None
  
    def getElementsByXpath(self, xpath):
        try:
            elements = self.driver.find_elements_by_xpath(xpath)
            for element in elements:
                if not element.is_displayed():
                    elements.remove(element)
                elif self.debug == True:
                    print("element finded:%s" % element.text)
            return elements
        except:
            return []

    def clickElementByXpath(self, xpath):
        try:
            self.getElementByXpath(xpath).click()
        except:
            return False
        return True
    
    def inputTextByXpath(self, xpath, text):
        try:
            self.getElementByXpath(xpath).send_keys(text)
        except:
            return False
        return True

    def getFakeElementByCssAfterDisplay(self, css_sellect):
        str = "return window.getComputedStyle(document.querySelector('%s'), ':after').display" % (css_sellect)
        try:
            result = self.driver.execute_script(str)
        except:
            return False
        if  result == 'none':
            return False
        else:
            return True
    
    def readCookie(self, filename : Optional[str] = "cookie.pkl"):
        if os.path.exists(filename):
            for cookie in pickle.load(open(filename, "rb")):
                if 'expiry' in cookie:
                    del cookie['expiry']
                self.__driver.add_cookie(cookie)
            self.__driver.refresh()
            return True
        return False

    def getIframeImgByXpath(self, iframe_xpath, el_xpath, screenshot_filename):
        # 先找iframe
        try:
            iframe = self.driver.find_element_by_xpath(iframe_xpath)
        except:
            if self.debug == True:
                print("找不到\t%s\t对应的iframe" % iframe_xpath)
            return None
        # 再找元素
        try:
            self.driver.switch_to.frame(iframe)
            self.driver.find_element_by_xpath(el_xpath).screenshot(screenshot_filename)

        except:
            if self.debug == True:
                print("iframe\t%s\t找不到\t%s\t对应的element" % (el_xpath, iframe_xpath))
                self.driver.switch_to_default_content()
                return None
                
        self.driver.switch_to_default_content()
        return True
        
    def saveCookie(self, filename : Optional[str] = "cookie.pkl"):
        pickle.dump( self.__driver.get_cookies() , open(filename,"wb"))
 
    # info example
    # info = {
    #     'tihao' : {'xpath': "//div[@class='pager']/span"},
    #     'tiliang' : {'xpath': "//div[@class='pager']", 're':r'(?<=\/)(\d{1,2})'},
    #     'tixing' : {'xpath': "//div[@class='q-header']"},
    #     'abcd' : {'list':True, 'xpath': "//div[@class='q-answers']/div", 're':r'(?<=[A-F]\s{0,2}\.\s{0,2})(\S{1,100})'},
    #     'tips' : {'xpath': "//div[@class='ant-popover-inner-content']/div"},
    #     'answer' :  {'list':True, 'xpath': "//div[@class='ant-popover-inner-content']/div/font"},
    #     'showtips' : {'xpath': "//div[@class='q-footer']/span"},
    #     'qbody_inputs' : {'xpath': '//div[@class="q-body"]/div/input'},
    #     'submit' : {'xpath': '//div[@class="action-row"]/button'}
    #     }    
    # 先将要读取的元素定义成一个字典，然后一次性读取所有信息,读取一次后,info会保存在类中
    def read_info(self, info):
        self.info = info
        data = {}
        # 循环每个定义好的元素,读取信息
        for i in info:
            data[i] = {}
            # 根据是列表还是单个元素决定如何获取
            if 'list' in info[i]:
                data[i]['elements'] = self.getElementsByXpath(info[i]['xpath'])
            else:
                data[i]['elements'] = [self.getElementByXpath(info[i]['xpath'])]
                if data[i]['elements'][0] == None:
                    data[i]['elements'] = []
            # pprint(data[i])
            # 为获取的元素的text
            data[i]['text'] = []
            for j in data[i]['elements']:
                if 're' in info[i]:
                    data[i]['text'].append(re.search(info[i]['re'], j.text).group())
                else:
                    data[i]['text'].append(j.text)
        self.info_data = data
        return data
    
    # 去点击对应成员变量info中定义的元素
    def click(self, el : str, index : int = 0):
        self.read_info(self.info)
        if el in self.info_data and len(self.info_data[el]['elements']) > index:
            try:
                self.info_data[el]['elements'][index].click()
            except:
                if self.debug == True:
                    print('%s\t%s该元素好像不能被点击!' %
                          (str(self.info_data[el]['elements'][index]), self.info_data[el]['text'][index]))
                return False
            return True
        if self.debug == True:
            print("没有找到对应的网页元素,请先设置成员变量info(字典)")
        return False
    
    # 输入信息
    def input(self, el : str, text : str, index : int = 0):
        self.read_info(self.info)
        if el in self.info_data and len(self.info_data[el]['elements']) > index:
            try:
                self.info_data[el]['elements'][index].send_keys(text)
            except:
                if self.debug == True:
                    print('%s\t%s该元素好像不能被输入!' %
                          (str(self.info_data[el]['elements'][index]), self.info_data[el]['text'][index]))
                return False
            return True
        if self.debug == True:
            print("没有找到对应的网页元素,请先设置成员变量info(字典)")
        return False
    
    def getText(self, el : str, index : int = 0):
        self.read_info(self.info)
        if el in self.info_data and len(self.info_data[el]['elements']) > index:
            try:
                ret =  self.info_data[el]['text'][index]
                if ret == "":
                    return None
                else:
                    return ret
            except:
                if self.debug == True:
                    print("获取text出错")
                return None

    def getTexts(self, el : str):
        self.read_info(self.info)
        if el in self.info_data:
            try:
                return self.info_data[el]['text']
            except:
                if self.debug == True:
                    print("获取texts出错")
                return []
            
    def textComp(self, str1 : str, str2: str):
        if difflib.SequenceMatcher(None, str1, str2).ratio() >= 0.95:
            return True
        return False
    
    def clickByText(self, els : str, text : str):
        self.read_info(self.info)
        if els in self.info_data and len(self.info_data['els']) >= 1:
            for i in range(self.info_data['els']):
                if self.textComp(text, self.info_data['els'][i]['text']):
                    try:
                        self.info_data[els]['elements'][i].click()
                        return True
                    except:
                        return False
                